AOP
关于动态代理,Spring中有两种实现方式:JDK动态代理
,CGLIB代理
.
JDK动态代理和CGLIB的区别:
JDK动态代理: 通过与代理类实现同一接口来实现代理.
- 在Spring中注入的时候,应该使用接口注入,而不能使用实现类注入.
- 在通过applicationContext来getBean的时候,不能通过
类.class
的方式获取bean.因为bean已经被JDK代理为Proxy类,不再是原始的类了.
CGLIB: 通过修改字节码来实现代理.
- 在Spring中注入时,可以使用接口或实现类来注入.
- 在通过applicationContext来getBean的时候,可以通过接口或实现类获取bean
Spring5中如果实现了接口,默认使用JDK动态代理
,如果继承类使用CGLIB代理
.
SpringBoot2.0中默认使用CGLIB代理
.如果想使用JDK代理,可以设置proxy-target-class=false来强制使用JDK代理.
proxy-target-class这个属性值在以下几个地方存在,也就是以下几种情况下bean会被代理:
1.@EnableAspectJAutoProxy
开启AOP功能,如果bean的方法存在切面,bean会被代理.Springboot2.0中默认使用CGLIB代理
.
可以在配置文件中设置spirng.aop.proxy-target-class=false
来强制使用JDK代理.在@EnableAspectJAutoProxy(proxyTargetClass = false)在Springboot2.0中已经不生效.
如果在配置文件中设置spring.aop.auto=false
,则AopAutoConfiguration自动配置不生效,此时如果使用@EnableAspectJAutoProxy则生效.代理方式取决于注解上指定的proxy-target-class的值.
2.@EnableCaching
3.@EnableTransactionManagement
开启事务功能
4.@EnableAsync:
如果类存在@Async,bean会被代理.代理方式取决于bean有没有实现接口
.当然,可以通过@EnableAsync(proxyTargetClass = true)
来设置使用@Async时强制使用CGLIB代理.
如果bean上同时存在切面和@Async.也就是@EnableAspectJAutoProxy和@EnableAsync同时生效,bean的代理方式取决于@EnableAspectJAutoProxy
.因为在为@Async生成代理的时候,如果bean已经被代理了,则不再进行代理,直接将@Async加入切面即可.
proxy-target-class配置
在Springboot2.0中,proxy-target-class可以在多个地方进行设置:
1.配置文件中设置spring.aop.proxy-target-class
2.配置类上注解@EnableAspectJAutoProxy,@EnableCaching,@EnableTransactionManagement,@EnableAsync
那么,这么多地方可以设置proxy-target-class,谁的优先级比较高,最终proxy-target-class的值由谁决定呢?
@EnableCaching,@EnableTransactionManagement,这两个注解会导入AutoProxyRegister
,而AutoProxyRegister类里面的registerBeanDefinitions方法,会扫描配置类上的注解,并向容器中注入InfrastructureAdvisorAutoProxyCreator
.
当@EnableTransactionManagement没有在配置类上显示指定时,springboot自动配置TransactionAutoConfigutartion(即使配置文件中spring.aop.auto=false
也会向容器注入TransactionAutoConfiguration配置类)会读取配置文件中spring.aop.proxy-target-class
,bean的代理方式由配置文件中的值决定.
@EnableAsync比较特殊,它只是执行AsyncAnnotationBeanPostProcessor
.而这个BeanPostProvessor的proxy-target-class只取决于@EnableAsync注解上的值.
当bean只存在@Async时,bean的代理方式完全由@EnableAsync注解上的proxy-target-class的值决定.
当bean存在@Async,又存在@Transactional时,bean的代理方式会被@EnableTransactionManagement上的proxy-target-class的值决定.
而@EnableAspectJAutoProxy会倒导入AspectJAutoProxyRegistrar
,而AspectJAutoProxyRegistrar类里面的registerBeanDefinitions方法会向容器中注入AnnotationAwareAspectJAutoProxyCreator
.
配置文件中的spring.aop.auto=true
相当于开启@EnableAspectJAutoProxy.如果配置文件中spring.aop.auto=true
,则AopAutoConfiguration
自动配置类会生效.相当于也会向容器中注入AnnotationAwareAspectJAutoProxyCreator
.
在注入AnnotationAwareAspectJAutoProxyCreator
后,会读取标记了注解@EnableAspectJAutoProxy上的proxy-target-class的值,不管是注解方式还是在配置文件中配置,只要有一个proxy-target-class的值是true,bean的代理方式就会使用CGLIB代理.
如果同时存在@EnableTransactionManagement|@EnableCaching|@EnableAspectJAutoProxy上,proxy-target-class的值由@EnableAspectJAutoProxy决定.
因为在AopConfigUtils类的静态代码块中,规定了优先级,AnnotationAwareAspectJAutoProxyCreator总是会覆盖前面的ProxyCreator.
1 | static { |
如果spring.aop.auto=false,也没有@EnableAspectJAutoProxy. 那么,只要有@EnableCaching|@EnableTransactionManagement中的proxy-target-class有一个值为true,就使用CGLIB代理,因为AutoProxyRegister
扫描到一个proxy-target-class的值为ture,就默认使用CGLIB代理了,而不会继续扫描第二个注解的proxy-target-class的值.
综上所述,如果一个bean只有@Async,bean的代理方式由@EnableAsync的proxy-target-class决定.
如果一个bean有事务,有AOP切面,bean的代理方式由@EnableAspectJAutoProxy决定(如果配置文件中spring.aop.auto=true
,则bean的代理方式由配置文件中spring.aop.proxy-target-class
的值决定).而在Spirngboot2.0中,spring.aop.proxy-target-class默认值是true,bean默认是CGLIB代理.
如果配置文件中spring.aop.auto=true
,默认使用CGLIB代理
.proxy-target-class的最终取值取决于两者,只要配置文件或配置类中有一个地方设置proxy-target-class=true,就使用CGLIB代理.只有两个地方都设置proxy-target-calss=fasle才使用JDK动态代理.
如果配置文件中spring.aop.auto=false
,proxy-target-class的值仅仅受注解配置影响.如果存在多个@Enablexxx注解,只要有一个proxy-target-classd的值为ture,就默认使用CGLIB代理.
@EnableTransactionManagement
@EnableTransactionManagement默认使用JDK动态代理,导入了TransactionManagementConfigurationSelector.
1 | (ElementType.TYPE) |
1 | public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { |
AutoProxyRegistrar:
1 | public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar { |
ProxyTransactionManagementConfiguration:
1 | false) (proxyBeanMethods = |